package app.engine.mc;

import app.engine.component.AbstractSearchEngine;
import app.engine.component.AsyncFileWriter;
import app.engine.component.FixedPatternMatcher;
import app.engine.entitys.Move;
import app.game.ChessPoint;
import app.game.enums.Chess;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class MCTreeSearchEngine extends AbstractSearchEngine {

    private AsyncFileWriter fileWriter;
    private FixedPatternMatcher fixedPatternMatcher = new FixedPatternMatcher();

    public static Integer searchTimes = 20000;
    public static Double ucbParam = 1d;

    @Override
    public ChessPoint search(Chess[][] chessBoard, Chess comChess, int step, List<ChessPoint> stepHistory) {
        // 第一步必定下中间
        if (step == 1) {
            return new ChessPoint(BOARD_LINE / 2, BOARD_LINE / 2);
        }
        // 匹配定式，如果有匹配到直接用
        ChessPoint matchPoint = fixedPatternMatcher.match(stepHistory, chessBoard, step);
        if (matchPoint != null) {
            return matchPoint;
        }
        initParam(step);
        System.out.println("-----------------------------------------------------------------------------------------");
        fileWriter = new AsyncFileWriter();
        // 创建UCT根节点
        MCTreeNode mcRoot = MCTreeNode.buildRootNode(chessBoard, comChess);
        // 记录初始时间
        long startTime = System.currentTimeMillis();
        int times = 0;
        // 如果搜索时间未到就继续执行
        while ((System.currentTimeMillis() - startTime) < searchTimes) {
            // 第一步，选择，循环遍历MCTree，直到碰到未激活节点或叶子节点
            MCTreeNode nowNode = selectNode(mcRoot, comChess, comChess);
            // 第二步，扩展，激活该节点，并展开子树
            nowNode.activeNode(comChess);
            // 第三步，模拟，随机对局直到终局
            double result = simulate(nowNode, comChess);
            // 第四步，反馈，自下而上更新结果直到根节点
            backPropagate(nowNode, result);
            times++;
            // 每5000次打印一下Tree到文件
//            if (times % 5000 == 0) {
//                writeToFile(mcRoot, times);
//            }
        }
        System.out.println("迭代次数: " + times);
        // 打印一下Tree到文件
        writeToFile(mcRoot, step);
        fileWriter.shutdown();
        // 选择平均收益最大的节点
        MCTreeNode selectedNode = selectBestNode(mcRoot);
        System.out.println("最终选择的走法为" + selectedNode.move.toString());
        // 打印一下决策链条
        System.out.println("决策链路：");
        printBestChain(mcRoot, comChess, comChess, 1);
        return new ChessPoint(selectedNode.move.getX(), selectedNode.move.getY());
    }

    private void initParam(int step) {
        if (step < 8) {
            searchTimes = 60000;
            ucbParam = 2d;
        } else if (step < 15) {
            searchTimes = 30000;
            ucbParam = 1d;
        } else if (step < 50) {
            searchTimes = 15000;
            ucbParam = 0.5d;
        } else {
            searchTimes = 5000;
            ucbParam = 0.1d;
        }
    }

    private MCTreeNode selectNode(MCTreeNode nowNode, Chess nowChess, Chess comChess) {
        // 第一遍遍历查找未激活节点，或叶子节点（终局）
        if (nowNode.childrenList == null) {
            return nowNode;
        }
        double totalTimes = 0;
        for (MCTreeNode child: nowNode.childrenList) {
            totalTimes += child.times;
            if (!child.isActive) {
                return child;
            }
        }
        // 子节点都激活，则计算UCB值，找到最大值（或最小值）对应的节点，如果有多个随机取
        List<MCTreeNode> greatNodes = new ArrayList<>();
        double greatUCBValue = 0d;
        boolean maxFlag = nowChess == comChess;
        for (MCTreeNode child: nowNode.childrenList) {
            if (greatUCBValue == 0d) {
                greatUCBValue = child.getUCBValue(totalTimes, maxFlag);
                greatNodes.add(child);
                continue;
            }
            // 按照当前层为极大或极小分别处理
            double ucbValue = child.getUCBValue(totalTimes, maxFlag);
            if (maxFlag && ucbValue > greatUCBValue) {
                greatUCBValue = child.getUCBValue(totalTimes, maxFlag);
                greatNodes = new ArrayList<>();
                greatNodes.add(child);
            } else if (ucbValue == greatUCBValue) {
                greatNodes.add(child);
            } else if (!maxFlag && ucbValue < greatUCBValue) {
                greatUCBValue = child.getUCBValue(totalTimes, maxFlag);
                greatNodes = new ArrayList<>();
                greatNodes.add(child);
            }
        }
        MCTreeNode selectedNode = null;
        if (greatNodes.size() > 1) {
            selectedNode = greatNodes.get(new Random().nextInt(greatNodes.size()));
        } else {
            selectedNode = greatNodes.get(0);
        }

        // 递归直到找到未激活节点，或者节点已经到终局
        return selectNode(selectedNode, nowChess.getReverse(), comChess);
    }

    private double simulate(MCTreeNode nowNode, Chess comChess) {
        // 进入模拟的节点一定激活，如果为终局，直接返回结果
        if (nowNode.finalFlag != -999d) {
            return nowNode.finalFlag;
        }
        // 模拟对局
        Chess[][] nowChessBoard = SearchHelper.copyChessBoard(nowNode.chessBoard);
        Chess nowChess = nowNode.nowChess;
        while (true) {
            List<Move> moves = SearchHelper.getMoves(nowChessBoard, nowChess);
            if (moves.size() <= 0) {
                return 0d;
            }
            Move selectedMove = moves.get(new Random().nextInt(moves.size()));
            nowChessBoard[selectedMove.getX()][selectedMove.getY()] = nowChess;
            if (SearchHelper.isEnd(nowChessBoard, selectedMove, nowChess)) {
                return nowChess == comChess ? 1 : -1;
            }
            nowChess = nowChess.getReverse();
        }
    }

    private void backPropagate(MCTreeNode nowNode, double result) {
        nowNode.value += result;
        nowNode.times += 1d;
        MCTreeNode node = nowNode;
        while (node.parent != null) {
            node = node.parent;
            node.value += result;
            node.times += 1d;
        }

    }

    private void writeToFile(MCTreeNode node, int times) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("--------------------------------------------------"+times+"-----------------------------------------------\n");
        printNode(node, buffer, 1);
        fileWriter.writeToFileAsync("D:/log.txt", buffer.toString());
    }

    private void printNode(MCTreeNode node, StringBuffer buffer, int step) {
        buffer.append("@");
        for (int i = 0; i < step; i++) {
            buffer.append("=>");
        }
        buffer.append("@");
        buffer.append(" " + node.toString() + "\n");
        if (node.childrenList != null && node.childrenList.size() > 0) {
            for (MCTreeNode child: node.childrenList) {
                printNode(child, buffer, step + 1);
            }
        }
    }

    private MCTreeNode selectBestNode(MCTreeNode mcRoot) {
        List<MCTreeNode> greatNodes = new ArrayList<>();
        double greatAvgValue = 0d;
        for (MCTreeNode child: mcRoot.childrenList) {
            if (greatAvgValue == 0d) {
                greatAvgValue = child.value / child.times;
                System.out.println("走法" + child + " 的平均收益为" + greatAvgValue);
                greatNodes.add(child);
                continue;
            }
            double avgValue = child.value / child.times;
            System.out.println("走法" + child + " 的平均收益为" + avgValue);
            if (avgValue > greatAvgValue) {
                greatAvgValue = avgValue;
                greatNodes = new ArrayList<>();
                greatNodes.add(child);
            } else if (avgValue == greatAvgValue) {
                greatNodes.add(child);
            }
        }
        MCTreeNode selectedNode = null;
        if (greatNodes.size() > 1) {
            selectedNode = greatNodes.get(new Random().nextInt(greatNodes.size()));
        } else {
            selectedNode = greatNodes.get(0);
        }
        return selectedNode;
    }

    private void printBestChain(MCTreeNode nowNode, Chess nowChess, Chess comChess, int step) {
        List<MCTreeNode> greatNodes = new ArrayList<>();
        double greatAvgValue = 0d;
        if (nowNode.times <= 1 || nowNode.childrenList == null || nowNode.childrenList.size() == 0) {
            printChessBoard(nowNode.chessBoard);
            return;
        }
        boolean maxFlag = nowChess == comChess;
        for (MCTreeNode child: nowNode.childrenList) {
            double avgValue = child.value / child.times;
            if (greatAvgValue == 0d) {
                greatAvgValue = avgValue;
                greatNodes.add(child);
                continue;
            }
            if (maxFlag && avgValue > greatAvgValue) {
                greatAvgValue = avgValue;
                greatNodes = new ArrayList<>();
                greatNodes.add(child);
            } else if (avgValue == greatAvgValue) {
                greatNodes.add(child);
            } else if (!maxFlag && avgValue < greatAvgValue) {
                greatAvgValue = avgValue;
                greatNodes = new ArrayList<>();
                greatNodes.add(child);
            }
        }
        MCTreeNode selectedNode = null;
        if (greatNodes.size() > 1) {
            selectedNode = greatNodes.get(new Random().nextInt(greatNodes.size()));
        } else {
            selectedNode = greatNodes.get(0);
        }
        for (int i = 0; i < step; i++) {
            System.out.print("=>");
        }
        System.out.println(selectedNode + " 的平均收益为" + greatAvgValue);
        printBestChain(selectedNode, nowChess.getReverse(), comChess, step + 1);
    }


}
